Weather Forecast with PixieDust

This notebook shows how to:

  1. use the Weather Company Data API to get weather forecast json data based on latitude and longitude
  2. convert this json data into a pandas DataFrame
  3. create a weather chart and map with matplotlib
  4. create a weather chart and map with PixieDust

Before running the notebook:

1. Load and install packages

First, uncomment the lines in the below cell and upgrade the pixiedust and bokeh packages. When this is done restart the kernel. You have to do this only once, or when there is an update available.

Then import the package needed to run this notebook.


In [1]:
#!pip install --upgrade pixiedust
#!pip install --upgrade bokeh

In [2]:
import requests
import json
import pandas as pd
import numpy as np
from datetime import datetime
import time
import pixiedust


Pixiedust database opened successfully
Pixiedust version 1.0.4

2. Get weather data

Find the latitude and longitude of your current location by running this magic javascript cell. Then fill in your Weather Company API credentials to load the weather forecast for where you are.


In [3]:
%%javascript
navigator.geolocation.getCurrentPosition(function(position) {
  console.log(position.coords.latitude, position.coords.longitude);
setTimeout(function() {
IPython.notebook.kernel.execute('lat="' + position.coords.latitude + '";')
IPython.notebook.kernel.execute('lon="' + position.coords.longitude + '";')
},5000)});


Wait a few seconds to run the second cell to allow the above geolocation function to run.


In [5]:
print(lat, lon)


('51.4505749', '-2.5821685999999997')

In [20]:
# @hidden_cell
# Weather company data API credentials
username='xxxxxx'
password='xxxxxx'

In [7]:
line='https://'+username+':'+password+\
    '@twcservice.mybluemix.net/api/weather/v1/geocode/'+\
    lat+'/'+lon+'/forecast/intraday/10day.json?&units=m'
r=requests.get(line)
weather = json.loads(r.text)

Uncomment and run the next cell to have a look what the json data file looks like.


In [8]:
#print json.dumps(weather, indent=4, sort_keys=True)

3. Convert json data to pandas DataFrame

Convert the data into a DataFrame with each timestep on a new row. Convert the timestamp into a datetime format and drop the columns that are not needed. See this Cheat sheet for date format conversions. Finally, convert the data type into numeric.


In [9]:
df = pd.DataFrame.from_dict(weather['forecasts'][0],orient='index').transpose()
for forecast in weather['forecasts'][1:]:
    df = pd.concat([df, pd.DataFrame.from_dict(forecast,orient='index').transpose()])

df['date'] = df['fcst_valid_local'].apply(lambda x: datetime.strptime(x, '%Y-%m-%dT%H:%M:%S+0100'))

df = df.drop(['expire_time_gmt','num','qualifier','qualifier_code'],1)   
df = df.drop(['fcst_valid','fcst_valid_local','icon_extd','wdir_cardinal'],1)   
df = df.drop(['subphrase_pt1','subphrase_pt2','subphrase_pt3','class'],1)   
df = df.drop(['daypart_name','phrase_12char','phrase_22char','phrase_32char'],1)   

df.dtypes


Out[9]:
pop                    object
wspd                   object
rh                     object
icon_code              object
clds                   object
wdir                   object
temp                   object
precip_type            object
dow                    object
date           datetime64[ns]
dtype: object

In [10]:
df[['pop','wspd','rh','clds','wdir','temp']] = df[['pop','wspd','rh','clds','wdir','temp']].apply(pd.to_numeric)
df.dtypes


Out[10]:
pop                     int64
wspd                    int64
rh                      int64
icon_code              object
clds                    int64
wdir                    int64
temp                    int64
precip_type            object
dow                    object
date           datetime64[ns]
dtype: object

As there seems to be an issue with the pop column (percentage of precipitation), create a new column rain.


In [11]:
df['rain'] = df['pop'].as_matrix()
df=df.drop('pop',1)

In [12]:
df.head()


Out[12]:
wspd rh icon_code clds wdir temp precip_type dow date rain
0 19 62 26 73 266 13 rain Friday 2017-04-28 13:00:00 1
0 17 74 29 23 257 9 rain Friday 2017-04-28 19:00:00 7
0 7 85 33 15 208 5 rain Saturday 2017-04-29 01:00:00 8
0 14 70 30 51 170 10 rain Saturday 2017-04-29 07:00:00 7
0 19 55 26 78 167 14 rain Saturday 2017-04-29 13:00:00 1

4. Plot data with matplotlib


In [13]:
df = df.set_index('date',drop=False)
df.head()


Out[13]:
wspd rh icon_code clds wdir temp precip_type dow date rain
date
2017-04-28 13:00:00 19 62 26 73 266 13 rain Friday 2017-04-28 13:00:00 1
2017-04-28 19:00:00 17 74 29 23 257 9 rain Friday 2017-04-28 19:00:00 7
2017-04-29 01:00:00 7 85 33 15 208 5 rain Saturday 2017-04-29 01:00:00 8
2017-04-29 07:00:00 14 70 30 51 170 10 rain Saturday 2017-04-29 07:00:00 7
2017-04-29 13:00:00 19 55 26 78 167 14 rain Saturday 2017-04-29 13:00:00 1

In [14]:
import matplotlib.pyplot as plt
import matplotlib
%matplotlib inline

fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(14, 8))

df['temp'].plot(ax=axes[1], color='#6EEDD8',lw=3.0,sharex=True)
axes[1].set_title('Temperature',loc='left',fontsize=20)

df['rain'].plot(ax=axes[0], kind='bar', color='#C93D79',lw=2.0,sharex=True)
axes[0].set_title('Chance of rain',loc='left',fontsize=20)


Out[14]:
<matplotlib.text.Text at 0x7f8d06da0210>

5. Create a temperature map for the UK


In [15]:
cities = [
    ('Exeter',50.7184,-3.5339),
    ('Truro',50.2632,-5.051),
    ('Carmarthen',51.8576,-4.3121),
    ('Norwich',52.6309,1.2974),
    ('Brighton And Hove',50.8225,-0.1372),
    ('Bristol',51.44999778,-2.583315472),
    ('Durham',54.7753,-1.5849),
    ('Llanidloes',52.4135,-3.5883),
    ('Penrith',54.6641,-2.7527),
    ('Jedburgh',55.4777,-2.5549),
    ('Coventry',52.42040367,-1.499996583),
    ('Edinburgh',55.94832786,-3.219090618),
    ('Cambridge',52.2053,0.1218),
    ('Glasgow',55.87440472,-4.250707236),
    ('Kingston upon Hull',53.7457,-0.3367),
    ('Leeds',53.83000755,-1.580017539),
    ('London',51.49999473,-0.116721844),
    ('Manchester',53.50041526,-2.247987103),
    ('Nottingham',52.97034426,-1.170016725),
    ('Aberdeen',57.1497,-2.0943),
    ('Fort Augustus',57.1448,-4.6805),
    ('Lairg',58.197,-4.6173),
    ('Oxford',51.7517,-1.2553),
    ('Inverey',56.9855,-3.5055),
    ('Shrewsbury',52.7069,-2.7527),
    ('Colwyn Bay',53.2932,-3.7276),
    ('Newton Stewart',54.9186,-4.5918),    
    ('Portsmouth',50.80034751,-1.080022218)]   

icons=[]
temps=[]
for city in cities:
    lat = city[1]
    lon = city[2]
    line='https://'+username+':'+password+'@twcservice.mybluemix.net/api/weather/v1/geocode/'+str(lat)+'/'+str(lon)+'/observations.json?&units=m'
    r=requests.get(line)
    weather = json.loads(r.text)    
    icons=np.append(icons,weather['observation']['wx_icon'])    
    temps=np.append(temps,weather['observation']['temp'])

In [16]:
dfmap = pd.DataFrame(cities, columns=['city','lat','lon'])    
dfmap['temp']=temps
dfmap['icon']=icons
dfmap.head()


Out[16]:
city lat lon temp icon
0 Exeter 50.7184 -3.5339 9 None
1 Truro 50.2632 -5.0510 11 28
2 Carmarthen 51.8576 -4.3121 10 28
3 Norwich 52.6309 1.2974 9 30
4 Brighton And Hove 50.8225 -0.1372 11 None

In [17]:
from mpl_toolkits.basemap import Basemap
from matplotlib.offsetbox import AnnotationBbox, OffsetImage
from matplotlib._png import read_png
from itertools import izip
import urllib

matplotlib.style.use('bmh')
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(10, 12))

# background maps
m1 = Basemap(projection='mill',resolution=None,llcrnrlon=-7.5,llcrnrlat=49.84,urcrnrlon=2.5,urcrnrlat=59,ax=axes[0])
m1.drawlsmask(land_color='dimgrey',ocean_color='dodgerBlue',lakes=True)

m2 = Basemap(projection='mill',resolution=None,llcrnrlon=-7.5,llcrnrlat=49.84,urcrnrlon=2.5,urcrnrlat=59,ax=axes[1])
m2.drawlsmask(land_color='dimgrey',ocean_color='dodgerBlue',lakes=True)

# weather icons map
for [icon,city] in izip(icons,cities):
    lat = city[1]
    lon = city[2]
    try:
        pngfile=urllib.urlopen('https://github.com/ibm-cds-labs/python-notebooks/blob/master/weathericons/icon'+str(int(icon))+'.png?raw=true')
        icon_hand = read_png(pngfile)
        imagebox = OffsetImage(icon_hand, zoom=.15)
        ab = AnnotationBbox(imagebox,m1(lon,lat),frameon=False) 
        axes[0].add_artist(ab)
    except:
        pass
        
# temperature map    
for [temp,city] in izip(temps,cities):
    lat = city[1]
    lon = city[2]
    if temp>12: 
        col='indigo'
    elif temp>10:
        col='darkmagenta'
    elif temp>8:
        col='red'
    elif temp>6:
        col='tomato'
    elif temp>4:
        col='turquoise'
            
    x1, y1 = m2(lon,lat)
    bbox_props = dict(boxstyle="round,pad=0.3", fc=col, ec=col, lw=2)
    axes[1].text(x1, y1, temp, ha="center", va="center",
                size=11,bbox=bbox_props)
    
plt.tight_layout()


6. Plot data with PixieDust

https://ibm-cds-labs.github.io/pixiedust/


In [18]:
display(df)


Hey, there's something awesome here! To see it, open this notebook outside GitHub, in a viewer like Jupyter

In [ ]: